#pragma once
#include "../Server.hpp"
#include "Util.hpp"
#include <regex>

class HttpRequest{
public:
    HttpRequest():_version("HTTP/1.1"){}
    ~HttpRequest(){}

    /*针对于中间部分报头的相关操作*/
    void SetHeaders(const std::string& key, const std::string& value){_headers[key] = value;}
    bool HasHeaders(const std::string& key){return _headers.find(key) != _headers.end();}
    std::string GetHeaders(const std::string& key){ return (HasHeaders(key)) ? _headers[key] : "";}

    /*针对于查询字符串的操作*/
    void SetParams(const std::string& key, const std::string& value){_headers[key] = value;}
    bool HasParams(const std::string& key){return _headers.find(key) != _headers.end();}
    std::string GetParams(const std::string& key){ return (HasHeaders(key)) ? _headers[key] : "";}

    /*正文长度*/
    size_t TextLength(){return HasHeaders("Content-Length") ? std::stol(_headers["Content-Length"]) : 0;}

    /*判断当前否为短连接*/
    bool Close(){return (HasHeaders("Connection") && (GetHeaders("Connection") == "keep-alive")) ? false : true;}

    /*重置当前请求*/
    void Reset(){
        _method.clear();
        _url.clear();
        _version = "HTTP/1.1";
        _headers.clear();
        _params.clear();
        _text.clear();
        std::smatch tmp;
        _matches.swap(tmp);
    }

public:
    std::string _method;                                        //请求方法
    std::string _url;                                           //资源路径
    std::string _version;                                       //HTTP版本
    std::unordered_map<std::string, std::string> _params;       //查询字符串
    std::unordered_map<std::string, std::string> _headers;      //HTTP请求的中间部分报头
    std::string _text;                                          //正文部分
    std::smatch _matches;                                       //正则表达式存储容器，提取资源路径
};





class HttpResponse{
public:
    HttpResponse():_statu_code(200), _is_redirect(false){}
    HttpResponse(int statu):_statu_code(statu), _is_redirect(false){}
    ~HttpResponse(){}

    /*针对于中间部分报头的相关操作*/
    void SetHeaders(const std::string& key, const std::string& value){_headers[key] = value;}
    bool HasHeaders(const std::string& key){return _headers.find(key) != _headers.end();}
    std::string GetHeaders(const std::string& key){ return (HasHeaders(key)) ? _headers[key] : "";}

    /*设置正文，顺带设置属性*/
    void SetContent(const std::string& text, const std::string& type = "text/html"){
        _text = text;
        SetHeaders("Content-Type", type);
    }

    /*判断是否是短连接*/
    bool Close(){return HasHeaders("Connection") && GetHeaders("Connection") == "keep-alive" ? false : true;}

    /*重置当前应答*/
    void Reset(){
        _statu_code = 200;
        _headers.clear();
        _text.clear();
        _is_redirect = false;
        _redirect_location.clear();
    }

public:
    int _statu_code;                                            //状态码 不需要状态描述了，因为可以通过Util获取
    std::unordered_map<std::string, std::string> _headers;      //HTTP请求的中间部分报头
    std::string _text;                                          //正文部分

    bool _is_redirect;                                          //是否重定向
    std::string _redirect_location;                             //重定向的地址
};


/*表示的是当前HTTP请求接受的状态*/
typedef enum{
    HTTP_RECV_ERROR,
    HTTP_RECV_LINE,
    HTTP_RECV_HEAD,
    HTTP_RECV_TEXT,
    HTTP_RECV_OVER
}HttpRecvStatu;

/*表示单一一个HTTP请求报文的长度的上限值*/
#define MAX_LENGTH 8192

/*HTTP上下文模块：记录请求接收进度*/
class HttpContext{
private:
    /*解析请求行*/
    bool ParseHttpLine(const std::string& line){
        //使用正则表达式来进行解析！
        //其实所谓的正则表达式：就是根据正则表达式规则，对照源字符串进行匹配和提取
        std::regex e("(GET|HEAD|POST|PUT|DELETE) ([^?]*)(?:\\?(.*))? (HTTP/1\\.[01])(?:\n|\r\n)?", std::regex::icase);
        //这里可能会有一个问题：如果传递的方法是get、post、head等小写，就会出错！
        //std::regex::icase这个标志位可以忽略大小写
        std::smatch matches;
        bool ret = std::regex_match(line, matches, e);
        if(!ret){
            _req_recv_statu = HTTP_RECV_ERROR; 
            _resp_statu = 400;  
            return false;            
        }


        //反之，此时的请求行是复合HTTP的请求行格式的
        _req._method = matches[1];
        std::transform(_req._method.begin(), _req._method.end(), _req._method.begin(), ::toupper);
        _req._url = Util::UrlDecode(matches[2], false);
        _req._version = matches[4];
        
        //针对于查询字符串我们需要特殊处理
        std::vector<std::string> query_array;
        //必须要进行解码，要不然找不到等号！而且因为是查询字符串参数，所以需要让空格转加号
        std::string query =  Util::UrlDecode(matches[3], true);
        Util::Split(query, "&", &query_array);   //按照"&"把所有的query进行分割
        for(auto& s : query_array){
            size_t pos = s.find('=');
            //如果没找到的话，就说明此时的请求行的query是错误的！
            if(pos == std::string::npos){
                _req_recv_statu = HTTP_RECV_ERROR;
                _resp_statu = 400;  
                return false;       
            }
            //反之能够进行分割 -> 设置
            std::string key = s.substr(0, pos), value = s.substr(pos + 1);
            _req.SetParams(key, value);
        }
        return true;

        //std::regex e("(GET|POST|HEAD|PUT|DELETE) ([^?]*)(?:\\?(.*))? (HTTP/1\\.[01])(?:\n|\r\n)?");

        //解析：	
        //eg: std::string str = "GET /index/login?usrname=hhh&passwd=wolaile HTTP/1.1\r\n";
        //
        // 1. (GET|POST|HEAD|PUT|DELETE) -> 提取的是GET POST HEAD PUT DELETE OPTION的其中一个 得到：GET
        // 2. 紧接着就是一个空格，匹配请求方法后的空格，但是没有用括号括起来，不会进行提取的
        // 3. ([^?]*)表示匹配所有的非问好字符，*表示匹配多次(要不然只会匹配一次)                 得到：/index/login
        // 4. (?:\\?)?   (?:...)表示只进行匹配但是不提取 ?是有特殊含义的，需要进行转义，但是\？在c++中也是转义，所以需要对斜杠再次转义 -> 真正的问号是 \\?
        //    (?:...)?表示的是：匹配1次或0次！  (?:...)内部的东西可能是没有的，如果强行匹配会出错
        // 5. (.*)表示的是匹配所有字符并提取
        // 6. 但是因为(.*)后面我加了一个空格，所以(.*)只会提取到：     usrname=hhh&passwd=wolaile
        // 7. (HTTP/1\\.[01]) 表示的是提取 HTTP/1. 开头的串  (\\.表示真正的点)  [01]表示的是提取后面0个或1个(因为不知道版本是什么)
        // 8. (?:\n|\r\n)? 表示的匹配 \n 或者 \r\n 0次或1次 但是不进行提取



        /* 0 : GET /index/login?usrname=hhh&passwd=wolaile HTTP/1.1
           1 : GET
           2 : /index/login
           3 : usrname=hhh&passwd=wolaile
           4 : HTTP/1.1 */
    }

    /*获取请求行*/
    bool RecvLine(Buffer* buffer){
        if(_req_recv_statu != HTTP_RECV_LINE) return false;
        //读取一行
        std::string line = buffer->GetOneLine();
        /*如果读取一行失败(还没有找到换行符)*/
        if(line.size() == 0) {
            //如果缓冲区内此时长度过长都没有一个换行符，那就说明有问题！
            if(buffer->ReadableSize() > MAX_LENGTH){
                _req_recv_statu = HTTP_RECV_ERROR;   //一旦更改这个状态，后序的流程就没办法再进行了
                _resp_statu = 414;                   //"URI Too Long"
                return false;
            }
            else return true;                        //等待下一次新数据到来
        }
        else{
            //读成功了，但是还是太长，也是不行的！
            if(line.size() > MAX_LENGTH){
                _req_recv_statu = HTTP_RECV_ERROR;   //一旦更改这个状态，后序就没办法再进行操作了
                _resp_statu = 414;                   //"URI Too Long"
                return false;
            }
            //读取成功，长度合法，需要对请求行做解析，然后加入到_req
            else{
                bool ret = ParseHttpLine(line);
                if(ret)  _req_recv_statu = HTTP_RECV_HEAD;    //解析成功，就需要进入处理报头的阶段
                return ret;                                   //没解析成功也不要紧，等待下一次就好了，状态不变，也没有出错
            }
        }
    }

    /*解析一行报头*/
    bool ParseHttpHead(std::string& header){
        if(header.back() == '\n') header.pop_back();
        if(header.back() == '\r') header.pop_back();

        size_t pos = header.find(": ");
        if(pos == std::string::npos){
            _req_recv_statu = HTTP_RECV_ERROR;   
            _resp_statu = 400;
            return false;
        }
        std::string key = header.substr(0, pos), value = header.substr(pos + 2);
        _req.SetHeaders(key, value);
        return true;
    }
 
    /*获取报头*/
    bool RecvHead(Buffer* buffer){
        if(_req_recv_statu != HTTP_RECV_HEAD) return false;
        /*需要不断地进行获取报头 格式是：key: value\r\nkey: value\r\n...*/
        while(1){
            std::string header = buffer->GetOneLine();
            if(header.size() == 0){
                if(buffer->ReadableSize() > MAX_LENGTH){
                    _req_recv_statu = HTTP_RECV_ERROR;   //一旦更改这个状态，后序就没办法再进行操作了
                    _resp_statu = 414;                   //"URI Too Long"
                    return false;
                }
                else return true;  
            }
            if(header.size() > MAX_LENGTH){
                _req_recv_statu = HTTP_RECV_ERROR;   //一旦更改这个状态，后序就没办法再进行操作了
                _resp_statu = 414;                   //"URI Too Long"
                return false;
            }
            //如果获取的一行单纯就是"\r\n" 或者 "\n"，那就说明报头全部读取完毕
            if(header == "\n" || header== "\r\n") break;
            
            bool ret = ParseHttpHead(header);
            if(!ret) return ret;
        }
        _req_recv_statu = HTTP_RECV_TEXT;           //进入处理正文的状态
        return true;
    }

    /*获取正文*/
    bool RecvText(Buffer* buffer){
        if(_req_recv_statu != HTTP_RECV_TEXT) return false;
        //1.获取正文，首先得知道当前正文总共有多长
        size_t text_len = _req.TextLength();
        if(text_len == 0){
            //如果长度为0，直接返回
            _req_recv_statu = HTTP_RECV_OVER;
            return true;
        }
        //2.因为之前可能没处理完，有一部分已经处理到req的正文部分了，所以我们需要手动计算此次需要读取的长度
        int need_len = text_len - _req._text.size();
        if(need_len > buffer->ReadableSize()){
            //当前buffer中可读的数据不够，那就先把能读的读出来，然后追加到正文部分
            _req._text.append(buffer->ReadPos(), buffer->ReadableSize());
            buffer->RIndexBackward(buffer->ReadableSize());
            return true;
        }
        //3.如果足够，那就可以读取出来，并且进行追加
        _req._text.append(buffer->ReadPos(), need_len);
        buffer->RIndexBackward(need_len); 
        _req_recv_statu = HTTP_RECV_OVER;
        return true;
    }

public:
    HttpContext():_resp_statu(200), _req_recv_statu(HTTP_RECV_LINE){}
    HttpContext(int code):_resp_statu(code), _req_recv_statu(HTTP_RECV_LINE){}
    ~HttpContext(){}

    int RespStatu(){return _resp_statu;}
    HttpRequest& Request(){return _req;}
    HttpRecvStatu RecvStatu(){return _req_recv_statu;}

    void Reset(){
        _resp_statu = 200;
        _req_recv_statu = HTTP_RECV_LINE;
        _req.Reset();
    }

    //本质上就是在做反序列化了
    void RecvHttpRequest(Buffer* buffer){
        //这里不能break！因为操作是根据当前状态连续进行的！
        switch(_req_recv_statu){
            case HTTP_RECV_LINE: RecvLine(buffer);
            case HTTP_RECV_HEAD: RecvHead(buffer);
            case HTTP_RECV_TEXT: RecvText(buffer);
        }
    }
private:
    int _resp_statu;                    //应答的状态码
    HttpRequest _req;                   //已接受的请求解析在这里
    HttpRecvStatu _req_recv_statu;      //当前HTTP请求已经处理到的阶段
};